//author: Panagiotis Theodoropoulos
package SC12Project.Milestone1.HotelReservation;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;

import org.example.hoteldb.BookingInfo;
import org.example.hoteldb.HotelInfo;
import org.example.hoteldb.ObjectFactory;
import org.example.hoteldb.RoomInfoLog;
import org.example.hoteldb.StayPeriodType;


import java.util.ArrayList;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.List;


public class LoadInfo implements HotelReservationWS{

	private static final String xml = "../datasource/ds_6_4.xml"; //this has been also tested
	//private static final String xml = "./datasource/ds_6_4_mytest.xml"; //this is my own XML
	
		
	public static Marshaller marshaller;
	public static Unmarshaller unmarshaller;
	public static File file;
	
	private static void configureJAXB() throws JAXBException{
		
		if (unmarshaller == null || marshaller == null) {
			
		    ClassLoader cl = org.example.hoteldb.ObjectFactory.class.getClassLoader();
		    String packageName = HotelInfo.class.getPackage().getName();
		    JAXBContext jc = JAXBContext.newInstance(packageName, cl);
			
			// create marshaller and unmarshaller using context
			unmarshaller = jc.createUnmarshaller();
			marshaller = jc.createMarshaller();
			file = new File(xml);
		}
	}
			
	private static int overlap(StayPeriodType period1, StayPeriodType period2) { 																				//period1 = given period to check  | period2 = already booked
		int overlap = 1;
		if ((period1.getCheckin().getYear() < period2.getCheckin().getYear()) && (period1.getCheckout().getYear() < period2.getCheckin().getYear()) || (period1.getCheckin().getYear() > period2.getCheckin().getYear()) && (period1.getCheckin().getYear() > period2.getCheckout().getYear()) ) {
			//System.out.println("year different\n");
			overlap = 0;
		}
		else if ((period1.getCheckin().getYear() == period2.getCheckin().getYear()) && (period1.getCheckout().getYear() == period2.getCheckin().getYear()) || (period1.getCheckin().getYear() == period2.getCheckin().getYear()) && (period1.getCheckin().getYear() == period2.getCheckout().getYear()) ) {
			//System.out.println("year not different\n");
			if ((period1.getCheckin().getMonth() < period2.getCheckin().getMonth()) && (period1.getCheckout().getMonth() < period2.getCheckin().getMonth()) || (period1.getCheckin().getMonth() > period2.getCheckin().getMonth()) && (period1.getCheckin().getMonth() > period2.getCheckout().getMonth())) {
				//System.out.println("month different\n");
				overlap = 0;
			}
			else if ((period1.getCheckin().getMonth() == period2.getCheckin().getMonth()) && (period1.getCheckout().getMonth() == period2.getCheckin().getMonth()) || (period1.getCheckin().getMonth() == period2.getCheckin().getMonth()) && (period1.getCheckin().getMonth() == period2.getCheckout().getMonth())) {
					//System.out.println("month not different\n");
				if ((period1.getCheckin().getDate() < period2.getCheckin().getDate()) && (period1.getCheckout().getDate() < period2.getCheckin().getDate()) || (period1.getCheckin().getDate() > period2.getCheckin().getDate()) && (period1.getCheckin().getDate() > period2.getCheckout().getDate())) {
					//System.out.println("date different\n");
					overlap = 0;
				}
			}
		}
		return overlap;
	}
	
	@Override
	public List<RoomInfo> getAvailableRooms(StayPeriod period) {
		System.out.println("getAvailableRooms function:");
		try {
			configureJAXB();
		} catch (JAXBException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		JAXBElement<HotelInfo> tmp = null;
		try {
			tmp = (JAXBElement<HotelInfo>)unmarshaller.unmarshal(file);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		HotelInfo hotel = tmp.getValue();
		
		int bookedrooms = 0;
		int j=0;
		
		for (int i = 0; i < hotel.getBookings().getBooking().size(); i++) {
			if (overlap(new StayPeriodType(period), hotel.getBookings().getBooking().get(i).getStayPeriod())==1) {										// check overlap
				//System.out.println("overlap");
				bookedrooms = hotel.getBookings().getBooking().get(i).getAmount();
				for (j=0; j<hotel.getRooms().getRoom().size(); j++) {																// for each booking type check every existing room type
					if (hotel.getBookings().getBooking().get(i).getType().equalsIgnoreCase(hotel.getRooms().getRoom().get(j).getType()) ) { 			//if same type is booked
						hotel.getRooms().getRoom().get(j).setTotalAmount(hotel.getRooms().getRoom().get(j).getTotalAmount() - bookedrooms); 	//reduce 1 room from the loaded list - change affects only memory - not XML
					}
				}
				bookedrooms = 0;
			}
		}
		return hotel.getRooms().getRoomInfo();
	}

	private List<RoomInfoLog> getAvailableRoomsLogic(StayPeriod period) {
		System.out.println("getAvailableRooms function:");
		try {
			configureJAXB();
		} catch (JAXBException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		JAXBElement<HotelInfo> tmp = null;
		try {
			tmp = (JAXBElement<HotelInfo>)unmarshaller.unmarshal(file);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		HotelInfo hotel = tmp.getValue();
		
		int bookedrooms = 0;
		int j=0;
		
		for (int i = 0; i < hotel.getBookings().getBooking().size(); i++) {
			if (overlap(new StayPeriodType(period), hotel.getBookings().getBooking().get(i).getStayPeriod())==1) {										// check overlap
				//System.out.println("overlap");
				bookedrooms = hotel.getBookings().getBooking().get(i).getAmount();
				for (j=0; j<hotel.getRooms().getRoom().size(); j++) {																// for each booking type check every existing room type
					if (hotel.getBookings().getBooking().get(i).getType().equalsIgnoreCase(hotel.getRooms().getRoom().get(j).getType()) ) { 			//if same type is booked
						hotel.getRooms().getRoom().get(j).setTotalAmount(hotel.getRooms().getRoom().get(j).getTotalAmount() - bookedrooms); 	//reduce 1 room from the loaded list - change affects only memory - not XML
					}
				}
				bookedrooms = 0;
			}
		}
		return hotel.getRooms().getRoom();
	}
	
	private String createbookingID() { // !!! already existing IDs are checked as String. New IDs are 1,2,3... integer but converted to string in order to conform with XML schema
		try {
			configureJAXB();
		} catch (JAXBException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		JAXBElement<HotelInfo> tmp = null;
		try {
			tmp = (JAXBElement<HotelInfo>)unmarshaller.unmarshal(file);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		HotelInfo hotel = tmp.getValue();
		String lastid;
		
		int size = hotel.getBookings().getBooking().size();
		int lid=0;
		if (size > 0) {
			lastid = hotel.getBookings().getBooking().get(size-1).getBookingID();
			
			try {
				lid=Integer.parseInt(lastid);	//check if string is number			
			}
			catch(Exception e) {
				lid = 1; //first booking
				lastid = Integer.toString(lid);
				return lastid;
			}
			lid++; //new id is the lastone + 1
			lastid = Integer.toString(lid);
		}
		else {
			lid = 1; //first booking
			lastid = Integer.toString(lid);
		}
		
		
		return lastid;
		
	}
	
	@Override
	public String bookRoom(String type, int amount, StayPeriod period) throws UnAvailableException {
		System.out.println("bookRoom function:");
		
		try {
			configureJAXB();
		} catch (JAXBException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
				
		JAXBElement<HotelInfo> tmp = null;
		try {
			tmp = (JAXBElement<HotelInfo>)unmarshaller.unmarshal(file);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
				
		HotelInfo hotel = tmp.getValue();
			    

		List<RoomInfoLog> list = new ArrayList<RoomInfoLog>();
		list = this.getAvailableRoomsLogic(period);	//from getavailablerooms
		
		String newID = null;
		BookingInfo newbooking = new BookingInfo();
		
		for (int i =0; i < list.size(); i++) {
			if (list.get(i).getType().equals(type)) { //same room type found available in the requested period
				if(list.get(i).getTotalAmount()>=amount) { //if requested amount is satisfyed
					newbooking.setAmount(amount);
					newbooking.setStayPeriod(new StayPeriodType(period));
					newbooking.setType(type);
					newID = createbookingID();
					newbooking.setBookingID(newID);
					hotel.getBookings().getBooking().add(newbooking);
				}
				else {
					//throw exception
					return "now rooms for the given type";
				}
			}
		}
				
		ObjectFactory obf = new ObjectFactory();
	    try {
	        JAXBElement<HotelInfo> output = obf.createHotel(hotel);
	        marshaller.marshal(output, file);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		return newID;
	}

	@Override
	public void cancelBooking(String bookingID) {
		System.out.println("cancelBooking");
		try {
			configureJAXB();
		} catch (JAXBException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		JAXBElement<HotelInfo> tmp = null;
		try {
			tmp = (JAXBElement<HotelInfo>)unmarshaller.unmarshal(file);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		HotelInfo hotel = tmp.getValue();

		for (int i = 0; i < hotel.getBookings().getBooking().size(); i++) {
				if(hotel.getBookings().getBooking().get(i).getBookingID().equals(bookingID)) {
					hotel.getBookings().getBooking().remove(i);
				}
		}
		
		ObjectFactory obf = new ObjectFactory();
	    try {
	        JAXBElement<HotelInfo> output = obf.createHotel(hotel);
	        marshaller.marshal(output, file);
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}

}
